package mazewar.legacy;


import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.swing.JOptionPane;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author jillsanluis
 */
public class NetworkClient implements Runnable, KeyListener {

    private static final boolean bypassGUI = false;
    private static Mazewar mazeWar;
    private Socket socket = null;
    private ObjectOutputStream streamOut = null;
    private Thread thread = null;
    private NetworkClientThread netClientThread = null;
    private boolean join = true;
    private String srcName;
    private ConcurrentLinkedQueue<ClientSessionInput> queue = new ConcurrentLinkedQueue<ClientSessionInput>();

    public NetworkClient(String srcName, String serverName, int serverPort) {
        System.out.println("Establishing connection. Please wait ...");
        try {
            socket = new Socket(serverName, serverPort);
            System.out.println("Connected: " + socket);

            // initialize the stream
            streamOut = new ObjectOutputStream(socket.getOutputStream());
            this.srcName = srcName;

            if (thread == null) {
                netClientThread = new NetworkClientThread(this, socket);
                thread = new Thread(this);
                thread.start();
            }

        } catch (UnknownHostException uhe) {
            System.out.println("Host unknown: " + uhe.getMessage());
        } catch (IOException ioe) {
            System.out.println("Unexpected exception: " + ioe.getMessage());
        }
    }

    public void run() {

        while (true) {

            try {

                NetworkPacket np = new NetworkPacket();
                np.setSrcName(this.srcName);
                np.setSrcSocket(socket.getLocalPort());
                if (join) {
                    np.setSrcInput(ClientSessionInput.JOIN);
                    // Create the GUI

                    join = false;
                } else if (!queue.isEmpty()) {
                    np.setSrcInput(queue.element());
                    queue.remove();
                }

                if (np.getSrcInput() != null) {
                    streamOut.writeObject(np);
                    streamOut.flush();
                }

            } catch (IOException e) {
                System.out.println("Sending error: " + e.getMessage());
                break;
            }
        }

        stop();

    }

    public void stop() {
        // close the streams and socket
        try {
            if (streamOut != null) {
                streamOut.close();
            }
            if (socket != null) {
                socket.close();
            }
        } catch (IOException e) {
            System.out.println("Error closing...");
        }

        netClientThread.close();
    }

    /**
     * Entry point for the game.
     * @param args Command-line arguments.
     */
    public static void main(String args[]) {

        if (args.length == 2) {
            String hostname = args[0];
            int port = Integer.parseInt(args[1]);

            // Retrieve the name of the player
            // Throw up a dialog to get the GUIClient name.

            String name = "bypass-gui-mode";
            if (!bypassGUI) {
                name = JOptionPane.showInputDialog("Enter your name");
                if ((name == null) || (name.length() == 0)) {
                    System.out.println("Invalid name. Exiting game...");
                    System.exit(-1);
                }
            }

            new NetworkClient(name, hostname, port);



        } else {
            System.err.println("ERROR: Invalid arguments!");
            System.exit(-1);
        }
    }

    public synchronized void handle(NetworkPacket packet, NetworkClient nc) {

        // ignore useless input
        if (packet.getSrcInput() == null) {
            return;
        }

        // determine if the client is local or remote
        boolean localClient = true;
        if (!packet.getSrcSocket().equals(packet.getDstSocket()) || !packet.getSrcName().equals(packet.getDstName())) {
            localClient = false;
        }

        String key = packet.getSrcName() + " " + packet.getSrcSocket().toString();

        if (packet.getSrcInput() == ClientSessionInput.JOIN) {

            if (!bypassGUI) {
		if(mazeWar == null){
                    mazeWar = new Mazewar(packet.getWindowWidth(), packet.getWindowHeight(), nc);
		}
            }
            if (localClient) {
                if (!bypassGUI) {
                    mazeWar.addPlayer(new GUIClient(packet.getSrcName()), packet.getSrcDirectedPoint(), key);
                }
                System.out.println("Added local player: " + key);
            } else {
                // TODO: fill in the impelmentation for RemoteClient
                if (!bypassGUI) {
                    mazeWar.addPlayer(new RemoteClient(packet.getSrcName()), packet.getSrcDirectedPoint(), key);
                }
                System.out.println("Added remote player: " + key);
            }
        } else if (packet.getSrcInput() == ClientSessionInput.QUIT) {

            if (localClient) {
                // TODO: fill in the implementation for quit
                if (!bypassGUI) {
                    Mazewar.quit();
                }
                System.out.println("Local player " + key + " is exiting...");
                this.stop();
            } else {
                if (!bypassGUI) {
                    mazeWar.removePlayer(key);
                }
                System.out.println("Removing remote player " + key);
            }

        } else if(packet.getSrcInput() == ClientSessionInput.REBORN) {

            if(mazeWar.getLocalClient() != null){
                mazeWar.rebornPlayer(packet.getSrcDirectedPoint(), key);
            }
        }
        else {

            if (!bypassGUI) {

                Client updatingClient = mazeWar.getPlayer(key);

if(updatingClient != null){
                if (packet.getSrcInput() == ClientSessionInput.DOWN) {
                    updatingClient.backup();
                } else if (packet.getSrcInput() == ClientSessionInput.UP) {
                    updatingClient.forward();
                } else if (packet.getSrcInput() == ClientSessionInput.RIGHT) {
                    updatingClient.turnRight();
                } else if (packet.getSrcInput() == ClientSessionInput.LEFT) {
                    updatingClient.turnLeft();
                } else if (packet.getSrcInput() == ClientSessionInput.SPACE) {
                    updatingClient.fire();
                }
}
            }

            System.out.println(key + "'s move is " + packet.getSrcInput());
        }

    }

    public void keyPressed(KeyEvent e) {
        // If the user pressed Q, invoke the cleanup code and quit.

        System.out.println("Key is pressed. " + e);


        if ((e.getKeyChar() == 'q') || (e.getKeyChar() == 'Q') || e.getKeyCode() == KeyEvent.VK_C) {
            queue.add(ClientSessionInput.QUIT);
            // Up-arrow moves forward.
        } else if (e.getKeyCode() == KeyEvent.VK_UP) {
            queue.add(ClientSessionInput.UP);
            // Down-arrow moves backward.
        } else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
            queue.add(ClientSessionInput.DOWN);
            // Left-arrow turns left.
        } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
            queue.add(ClientSessionInput.LEFT);
            // Right-arrow turns right.
        } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            queue.add(ClientSessionInput.RIGHT);
            // Spacebar fires.
        } else if (e.getKeyCode() == KeyEvent.VK_SPACE) {
            queue.add(ClientSessionInput.SPACE);
        }
    }

    public void keyReleased(KeyEvent e) {
    }

    public void keyTyped(KeyEvent e) {
    }

    public void clientReborn() {
        try {

            System.out.println(this.srcName + " reincarnation...");
            NetworkPacket np = new NetworkPacket();
            np.setSrcName(this.srcName);
            np.setSrcSocket(socket.getLocalPort());
            np.setSrcInput(ClientSessionInput.REBORN);

            streamOut.writeObject(np);
            streamOut.flush();

        } catch (IOException e) {
            System.out.println("Sending error: " + e.getMessage());

        }
    }
}
